package com.cetccloud.ap.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.cetccloud.ap.common.SafeChecker;
import com.cetccloud.ap.config.ProxyConfig;
import com.cetccloud.ap.controller.model.ApiResult;
import com.cetccloud.ap.controller.model.req.HttpReq;
import com.cetccloud.ap.controller.model.req.SubscribeReq;
import com.cetccloud.ap.controller.model.resp.SubscribeResp;
import com.cetccloud.ap.proxy.channel.Channel;
import com.cetccloud.ap.proxy.channel.cossDomain.model.CallTypeEnum;
import com.cetccloud.ap.proxy.channel.cossDomain.model.CrossDomainModel;
import com.cetccloud.ap.proxy.channel.cossDomain.model.FileXmlModel;
import com.cetccloud.ap.proxy.coord.Coord;
import com.cetccloud.ap.service.ProxyService;
import com.cetccloud.ap.util.IdUtil;
import com.cetccloud.ap.util.exception.ApException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import javax.validation.Valid;
import java.time.Duration;

/**
 * @ClassName ProxyController
 * @Description TODO
 * @Author superz
 * @Date 2021/4/9 15:27
 * @Version 1.0
 **/
@Slf4j
@RestController
@RequestMapping
public class ProxyController {

    @Autowired
    private ProxyService proxyService;

    @Autowired
    private Channel crossDomainChannel;

    @Autowired
    @Qualifier("srcCoord")
    private Coord srcCoord;

    @Autowired
    private ProxyConfig proxyConfig;

    @Autowired
    private SafeChecker safeChecker;

    @PostMapping("/http")
    public Mono<?> http(@Valid @RequestBody HttpReq httpReq) {
        log.info("/proxy/http: {}", JSON.toJSONString(httpReq));
        // 安全校验
        if (!safeChecker.httpCanProxy(httpReq.getUrl())) {
            log.error("请求访问的url不在白名单中: " + httpReq.getUrl());
            return Mono.error(new ApException("请求访问的url不在白名单中: " + httpReq.getUrl()));
        }

        return Mono.create(sink -> {
            String transId = IdUtil.transId();
            srcCoord.registerSink(transId, sink);

            // 发起跨域请求
            CrossDomainModel model = new CrossDomainModel();
            model.setTransId(transId);
            model.setCallType(CallTypeEnum.HTTP_REQ);
            model.setPayload(BeanUtil.beanToMap(httpReq));
            crossDomainChannel.message(httpReq.getDestZoneId(), model);
        }).timeout(Duration.ofSeconds(10));
    }

    @PostMapping("/http/subscribe")
    public Mono<ApiResult<SubscribeResp>> httpSubscribe(@Valid @RequestBody SubscribeReq subscribeReq) {
        log.info("/proxy/http/subscribe: {}", subscribeReq);

        return Mono.create(_sink -> Mono.create(sink -> {
            String transId = IdUtil.transId();
            srcCoord.registerSink(transId, sink);

            // 发起跨域请求
            CrossDomainModel model = new CrossDomainModel();
            model.setTransId(IdUtil.transId());
            model.setCallType(CallTypeEnum.HTTP_REQ_SUBSCRIBE);
            model.setPayload(BeanUtil.beanToMap(subscribeReq));
            crossDomainChannel.message(subscribeReq.getDestZoneId(), model);
        }).timeout(Duration.ofSeconds(10))
                .map(resp -> JSON.parseObject(JSON.toJSONString(resp), SubscribeResp.class))
                .subscribe(resp -> {
                    Mono mono = proxyService._createTopic(resp.getTopic());
                    mono.subscribe(v -> _sink.success(resp),
                            t -> _sink.error(new ApException((Throwable) t)));
                })).map(subscribeResp -> ApiResult.success().build(subscribeResp));

//        return Mono.create(sink -> srcCoord.registerSink(model.getTransId(), sink))
//                .timeout(Duration.ofSeconds(10))
//                .map(resp -> JSON.parseObject(JSON.toJSONString(resp), SubscribeResp.class))
//                // 在本域创建队列
//                .handle((subscribeResp, synchronousSink) -> {
//                    boolean flag = proxyService.createTopic(subscribeResp.getTopic());
//                    if (flag) {
//                        synchronousSink.next(subscribeResp);
//                    } else {
//                        synchronousSink.error(new ApException("创建本域kafka消息队列失败"));
//                    }
//                })
//                .map(subscribeResp -> ApiResult.success().build(subscribeResp));
    }

//    @PostMapping("/file/send")
//    public Mono<ApiResult<String>> fileSend(@RequestPart("destZoneId") String destZoneId,
//                                            @RequestPart("fileName") String fileName, @RequestPart("file") FilePart filePart) throws IOException {
//        log.info("/proxy/file, destZoneId: {}, fileName: {}", destZoneId, fileName);
//        String uploadPath = proxyConfig.getFile().getPath();
//        // 重命名文件
//        String newFileName = System.currentTimeMillis() + "_" + fileName;
//        Path localFile = Files.createFile(Paths.get(uploadPath + newFileName));
////        filePart.transferTo(localFile.toFile());
//        AsynchronousFileChannel channel =
//                AsynchronousFileChannel.open(localFile, StandardOpenOption.WRITE);
//        DataBufferUtils.write(filePart.content(), channel, 0)
//                .doOnComplete(() -> {
//                    log.info("文件上传至: {}", localFile);
//                    FileXmlModel fileXmlModel = new FileXmlModel();
//                    fileXmlModel.setFileName(localFile.toFile().getName());
//                    fileXmlModel.setFileSize(localFile.toFile().length());
//                    crossDomainChannel.fileXml(destZoneId, fileXmlModel);
//                    try {
//                        channel.close();
//                    } catch (IOException e) {
//                        log.error("文件流关闭失败");
//                    }
//                }).subscribe();
//        return Mono.just(ApiResult.success().build(newFileName));
//    }


    @GetMapping("/file/send")
    public Mono<ApiResult<String>> fileSend(@RequestParam("destZoneId") String destZoneId,
                                            @RequestParam("fileName") String fileName,
                                            @RequestParam("fileSize") long fileSize) {
        log.info("/proxy/file, destZoneId: {}, fileName: {}", destZoneId, fileName);
        FileXmlModel fileXmlModel = new FileXmlModel();
        fileXmlModel.setFileName(fileName);
        fileXmlModel.setFileSize(fileSize);
        crossDomainChannel.fileXml(destZoneId, fileXmlModel);
        return Mono.just(ApiResult.success());
    }

//    @GetMapping("/file/download")
//    public Mono<Void> fileDownload(@RequestParam("fileName") String fileName, ServerHttpResponse response) {
//        Path path = Paths.get(proxyConfig.getFile().getPath() + fileName);
//        if (!path.toFile().exists()) {
//            response.setStatusCode(HttpStatus.NOT_FOUND);
//            return response.setComplete();
//        }
//
//        ZeroCopyHttpOutputMessage zeroCopyResponse = (ZeroCopyHttpOutputMessage) response;
//        response.getHeaders().set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + StrUtil.str(StrUtil.bytes(fileName, "utf-8"), "ISO_8859_1"));
//        response.getHeaders().setContentType(MediaType.APPLICATION_OCTET_STREAM);
//        response.getHeaders().add("charset", "utf-8");
//        return zeroCopyResponse.writeWith(path, 0, path.toFile().length());
//    }

}
